import {
  hasOwn,
  isPlainObject
} from 'uni-shared'

const TAGS = {
  'a': '',
  'abbr': '',
  'b': '',
  'blockquote': '',
  'br': '',
  'code': '',
  'col': ['span', 'width'],
  'colgroup': ['span', 'width'],
  'dd': '',
  'del': '',
  'div': '',
  'dl': '',
  'dt': '',
  'em': '',
  'fieldset': '',
  'h1': '',
  'h2': '',
  'h3': '',
  'h4': '',
  'h5': '',
  'h6': '',
  'hr': '',
  'i': '',
  'img': ['alt', 'src', 'height', 'width'],
  'ins': '',
  'label': '',
  'legend': '',
  'li': '',
  'ol': ['start', 'type'],
  'p': '',
  'q': '',
  'span': '',
  'strong': '',
  'sub': '',
  'sup': '',
  'table': ['width'],
  'tbody': '',
  'td': ['colspan', 'rowspan', 'height', 'width'],
  'tfoot': '',
  'th': ['colspan', 'rowspan', 'height', 'width'],
  'thead': '',
  'tr': '',
  'ul': ''
}
const CHARS = {
  'amp': '&',
  'gt': '>',
  'lt': '<',
  'nbsp': ' ',
  'quot': '"',
  'apos': "'"
}

function decodeEntities (htmlString) {
  return htmlString.replace(/&(([a-zA-Z]+)|(#x{0,1}[\da-zA-Z]+));/gi, function (match, stage) {
    if (hasOwn(CHARS, stage) && CHARS[stage]) {
      return CHARS[stage]
    }
    if (/^#[0-9]{1,4}$/.test(stage)) {
      return String.fromCharCode(stage.slice(1))
    }
    if (/^#x[0-9a-f]{1,4}$/i.test(stage)) {
      return String.fromCharCode('0' + stage.slice(1))
    }
    let wrap = document.createElement('div')
    wrap.innerHTML = match
    return wrap.innerText || wrap.textContent
  })
}

export default function parseNodes (nodes, parentNode) {
  nodes.forEach(function (node) {
    if (!isPlainObject(node)) {
      return
    }
    if (!hasOwn(node, 'type') || node.type === 'node') {
      if (!(typeof node.name === 'string' && node.name)) {
        return
      }
      const tagName = node.name.toLowerCase()
      if (!hasOwn(TAGS, tagName)) {
        return
      }
      const elem = document.createElement(tagName)
      if (!elem) {
        return
      }
      const attrs = node.attrs
      if (isPlainObject(attrs)) {
        const tagAttrs = TAGS[tagName] || []
        Object.keys(attrs).forEach(function (name) {
          const value = attrs[name]
          switch (name) {
            case 'class':
            case 'style':
              elem.setAttribute(name, value)
              break
            default:
              if (tagAttrs.indexOf(name) !== -1) {
                elem.setAttribute(name, value)
              }
          }
        })
      }

      const children = node.children
      if (Array.isArray(children) && children.length) {
        parseNodes(node.children, elem)
      }

      parentNode.appendChild(elem)
    } else {
      if (node.type === 'text' && typeof node.text === 'string' && node.text !== '') {
        parentNode.appendChild(document.createTextNode(decodeEntities(node.text)))
      }
    }
  })
  return parentNode
}
